﻿using System;
using System.ComponentModel;
using DeepEarth.Core.Utilities;
using DeepEarth.Map.Core;
using DeepEarth.Toolkit.Drawing;

namespace DeepEarth.Toolkit.Controls
{
    public class MeasureToolModel : INotifyPropertyChanged
    {
        FreeDraw freeDraw;
        ShapeEditor shapeEditor;
        DistanceLabeler distanceLabeller;
        DynamicLayer drawLayer;

        public MeasureToolModel(IMap map, DynamicLayer drawLayer)
        {
            this.Map = map;
            this.drawLayer = drawLayer;
            MeasurementSystem = MeasurementSystem.Metric;
        }

        #region Properties
        public IMap Map { get; set; }

        MeasurementSystem measurementSystem;
        public MeasurementSystem MeasurementSystem
        {
            get { return measurementSystem; }
            set
            {
                if (value != measurementSystem)
                {
                    distance.MeasurementSystem = area.MeasurementSystem = measurementSystem = value;
                    OnPropertyChanged("MeasurementSystem", "TotalDistance", "Area", "DistanceUnit", "AreaUnit");
                }
            }
        }

        public MeasurementSystem[] AllMeasurementSystems
        {
            get { return new[] { MeasurementSystem.Metric, MeasurementSystem.Imperial }; }
        }

        public bool IsValidShape()
        {
            return freeDraw != null && freeDraw.FreeDrawModel.Locations.Count >= 2;
        }

        public bool CanResume
        {
            get { return freeDraw.CanResume; }
        }

        public bool CanClear
        {
            get { return freeDraw.FreeDrawModel.Locations != null && freeDraw.FreeDrawModel.Locations.Count > 0; }
        }

        DistanceMeasurement distance = new DistanceMeasurement();
        public Measurement Distance
        {
            get { return distanceLabeller == null ? null : distance; }
        }

        AreaMeasurement area = new AreaMeasurement();
        public Measurement Area
        {
            get { return distanceLabeller == null ? null : area; }
        }
        #endregion

        public void BeginMeasure()
        {
            freeDraw = new FreeDraw(this.Map.InteractionSurface, this.drawLayer, Map.LocationToViewportPoint, Map.ViewportPointToLocation);

            WeakEventListener<MeasureToolModel, FreeDraw> weakListener = new WeakEventListener<MeasureToolModel, FreeDraw>(this, freeDraw);
            freeDraw.DrawComplete += weakListener.OnEvent;

            weakListener.DetatchAction = (listener, source) =>
            {
                source.DrawComplete -= listener.OnEvent;
            };

            weakListener.EventAction = (listener, source, eventArgs) =>
            {
                listener.OnDrawComplete();
            };

            freeDraw.BeginFreeDraw();

            shapeEditor = new Drawing.ShapeEditor(this.Map.InteractionSurface, this.drawLayer, Map.LocationToViewportPoint, Map.ViewportPointToLocation);
            shapeEditor.Edit(freeDraw.PreviewDrawModel.Locations);

            // Associate the distance labeller with the preview draw model because we want to calculate distance as the mouse moves
            distanceLabeller = new DistanceLabeler(this.drawLayer, freeDraw.PreviewDrawModel.Locations, Map.LocationToViewportPoint, Map.ViewportPointToLocation);

            distanceLabeller.PropertyChanged += (o, e) =>
                {
                    OnPropertyChanged(e.PropertyName);
                    switch (e.PropertyName)
                    {
                        case "TotalDistance":
                            distance.RawValue = distanceLabeller.TotalDistance;
                            break;
                        case "Area":
                            area.RawValue = distanceLabeller.Area;
                            break;
                    }
                };
        }

        public void EndMeasure()
        {
            if (freeDraw != null)
            {
                freeDraw.EndFreeDraw();
            }
        }

        public void Clear()
        {
            if (this.freeDraw != null)
            {
                freeDraw.Clear();
            }

            if (this.shapeEditor != null)
            {
                this.shapeEditor.End();
            }
        }

        public void Resume()
        {
            if (this.freeDraw != null)
            {
                freeDraw.ResumeFreeDraw();
            }
        }

        #region Events
        public EventHandler DrawComplete;
        public void OnDrawComplete()
        {
            if (DrawComplete != null)
            {
                DrawComplete(this, new EventArgs());
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(params string[] propertyNames)
        {
            if (PropertyChanged != null)
            {
                foreach (var prop in propertyNames)
                {
                    string relay = prop;
                    if (prop == "TotalDistance")
                        relay = "Distance";
                    PropertyChanged(this, new PropertyChangedEventArgs(relay));
                }
            }
        }
        #endregion
    }
}